feat(FR-2616): handle totp and concurrent-session inline in STokenLoginBoundary#6897
Merged
graphite-app[bot] merged 1 commit intoApr 24, 2026
Conversation
Contributor
Author
5 tasks
Contributor
Coverage report for
|
St.❔ |
Category | Percentage | Covered / Total |
|---|---|---|---|
| 🔴 | Statements | 9.02% (+0.1% 🔼) |
1857/20587 |
| 🔴 | Branches | 8.19% (+0.11% 🔼) |
1187/14502 |
| 🔴 | Functions | 5.29% (+0.01% 🔼) |
295/5578 |
| 🔴 | Lines | 8.76% (+0.11% 🔼) |
1749/19973 |
Show files with reduced coverage 🔻
St.❔ |
File | Statements | Branches | Functions | Lines |
|---|---|---|---|---|---|
| 🟡 | ... / STokenLoginBoundary.tsx |
70.92% (-14.14% 🔻) |
60.22% (-31.68% 🔻) |
47.62% (-21.61% 🔻) |
72.99% (-13.05% 🔻) |
Test suite run success
865 tests passing in 40 suites.
Report generated by 🧪jest coverage report action from 7005b4b
c80fed0 to
c9ec6b3
Compare
b66b6dc to
8a99fb1
Compare
a231db5 to
17f7358
Compare
8a99fb1 to
8b238b7
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Extends STokenLoginBoundary to handle interactive auth challenges (TOTP and concurrent-session force login) inline within the existing error card UI, while improving token-login failure signaling and adding supporting i18n/E2E coverage.
Changes:
- Add structured token-login failure propagation (
fail_reason/fail_type) and throwTokenLoginFailedErroronauthenticated: false. - Implement inline TOTP submit flow + concurrent-session “force” retry flow in
STokenLoginBoundary, including improved pending-step messaging. - Add/adjust translations across multiple locales and expand Playwright E2E coverage for the interactive flows.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/lib/backend.ai-client-esm.ts | Surface fail_type alongside fail_reason from token_login for downstream classification. |
| react/src/helper/loginSessionAuth.ts | Introduce TokenLoginFailedError and ensure token-login failures can’t proceed to connectViaGQL. |
| react/src/components/STokenLoginBoundary.tsx | Add inline TOTP + concurrent-session force flows, failure classification, and pending-step messaging. |
| e2e/auth/stoken-login.spec.ts | Add E2E tests mocking /server/token-login to validate OTP/force retry request bodies and UI branching. |
| resources/i18n/en.json | Update/add sTokenLoginBoundary strings for new pending-step + TOTP flows and revised concurrent-session copy. |
| resources/i18n/ko.json | Update/add sTokenLoginBoundary strings for new pending-step + TOTP flows and revised concurrent-session copy. |
| resources/i18n/de.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/el.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/es.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/fi.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/fr.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/id.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/it.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/ja.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/mn.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/ms.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/pl.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/pt.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/pt-BR.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/ru.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/th.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/tr.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/vi.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/zh-CN.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
| resources/i18n/zh-TW.json | Add sTokenLoginBoundary strings for pending-step + interactive flows. |
17f7358 to
a1a4a7a
Compare
8b238b7 to
2d4b07a
Compare
a1a4a7a to
6f5b6cd
Compare
2d4b07a to
57e7a40
Compare
6f5b6cd to
abeb16c
Compare
8769ded to
33f5bd1
Compare
Merge activity
|
33f5bd1 to
aec9ce5
Compare
abeb16c to
d650bff
Compare
…inBoundary (#6897) Follow-up feature on top of Epic [FR-2616](https://lablup.atlassian.net/browse/FR-2616). Jira ticket to be filed. ## Summary Extends `STokenLoginBoundary` to handle two interactive auth challenges inline, without a modal and without splitting into separate components — the existing `DefaultErrorCard` layout stays, only its action area swaps per error kind. ### UX | Error kind | Card status | Description | Actions | |---|---|---|---| | `totp-required` *(new)* | `warning` | "Enter the 6-digit code from your authenticator app…" | Inline OTP `<Input>` + **Submit** | | `concurrent-session` *(activated)* | `warning` | "This account is already signed in somewhere else. Do you want to end the other session and continue here?" | **Copy details** · **Sign in anyway** | | all other kinds | `error` | (unchanged) | **Copy details** · **Retry** | On **Submit**, `STokenLoginBoundary` folds `otp: <entered>` into `extraParams` and replays the auth sequence. A failed retry re-renders the OTP card with an invalid-code hint above the input; the OTP itself is single-use (cleared after every retry). On **Sign in anyway**, the boundary sets a sticky `forceApprovedRef` and replays the sequence with `force: true` folded in. Sticky matches LoginView's `forceLoginApprovedRef` — a TOTP challenge that arrives *after* a force approval keeps the force flag on the subsequent retries. ### Why - `token_login` previously had a latent bug: `client.token_login` returns `{ fail_reason }` on `authenticated: false`, which is truthy, so the old `!loginSuccess` guard passed through and `connectViaGQL` ran against an unauthenticated client. Introducing `TokenLoginFailedError` fixes this at the helper layer before the classifier inspects it. - Classification uses duck-typed field extraction (`err.failReason`, `err.failType`) rather than `instanceof TokenLoginFailedError` so cross-module-instance errors (Jest mocks, HMR reloads) classify identically to direct throws. - String-match detection mirrors `LoginView.handleLoginError`'s legacy fallback so both entry points classify the same until the webserver surfaces the probe `type` through `client.token_login` (tracked by `TODO(user-tunable)`). ## Changes - `react/src/helper/loginSessionAuth.ts` - `TokenLoginFailedError` class and structured throw from `tokenLogin`. - `extraParams` type widened to `Record<string, string | boolean>` so `force: true` can pass through alongside string URL params. - `react/src/components/STokenLoginBoundary.tsx` - `STokenLoginError` union: `totp-required` added, `concurrent-session` wired up end-to-end. - `pendingOtpRef` (single-use) + `forceApprovedRef` (sticky) retry state. - `retryWithOtp` / `retryWithForce` handlers; existing `retry()` preserved for non-interactive kinds. - `DefaultErrorCard` gains `onSubmitOtp` / `onConfirmForce` plumbing and kind-branched action area. Card `status` shifts to `warning` for the two interactive kinds. - New `TotpInlineForm` subcomponent (kept co-located in the same file, per scope request). - `resources/i18n/en.json`, `resources/i18n/ko.json` - `ErrorTotpRequiredTitle` / `ErrorTotpRequiredDescription` / `ErrorTotpInvalidHint` / `TotpPlaceholder` / `SubmitOtp` / `ForceLogin`. - `ErrorConcurrentSessionDescription` updated to match the new force-confirm UX. ## Test plan - [x] `bash scripts/verify.sh` → `ALL PASS` - [x] `pnpm --dir react jest src/components/STokenLoginBoundary.test.tsx` → 9 passed - [ ] Manual: navigate `/?sToken=<TOTP-required>` → card shows OTP input, submit retries with `otp=…`. Wrong code surfaces invalid hint; right code proceeds. - [ ] Manual: navigate with an active second-session setup → card offers "Sign in anyway"; confirming retries with `force=true`. - [ ] Manual (combined): sToken where the server first issues `active-login-session-exists` then `require-totp-authentication` on the force retry → force flag stays on while OTP is submitted. ## Follow-ups - File a Jira sub-task under Epic FR-2616 for this feature. - Swap string-match classifier for strict `type` equality once `client.token_login` surfaces the probe `type` to the caller. `TokenLoginFailedError.failType` is already reserved. - Translations for the 20 non-en/ko locales via the `fw:i18n` skill. [FR-2616]: https://lablup.atlassian.net/browse/FR-2616?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
d650bff to
d7fb3b2
Compare
aec9ce5 to
7005b4b
Compare
Base automatically changed from
04-22-test_fr-2639_add_e2e_regression_for_stoken_login_boundary_routes
to
main
April 24, 2026 07:50
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Follow-up feature on top of Epic FR-2616. Jira ticket to be filed.
Summary
Extends
STokenLoginBoundaryto handle two interactive auth challenges inline, without a modal and without splitting into separate components — the existingDefaultErrorCardlayout stays, only its action area swaps per error kind.UX
totp-required(new)warning<Input>+ Submitconcurrent-session(activated)warningerrorOn Submit,
STokenLoginBoundaryfoldsotp: <entered>intoextraParamsand replays the auth sequence. A failed retry re-renders the OTP card with an invalid-code hint above the input; the OTP itself is single-use (cleared after every retry).On Sign in anyway, the boundary sets a sticky
forceApprovedRefand replays the sequence withforce: truefolded in. Sticky matches LoginView'sforceLoginApprovedRef— a TOTP challenge that arrives after a force approval keeps the force flag on the subsequent retries.Why
token_loginpreviously had a latent bug:client.token_loginreturns{ fail_reason }onauthenticated: false, which is truthy, so the old!loginSuccessguard passed through andconnectViaGQLran against an unauthenticated client. IntroducingTokenLoginFailedErrorfixes this at the helper layer before the classifier inspects it.err.failReason,err.failType) rather thaninstanceof TokenLoginFailedErrorso cross-module-instance errors (Jest mocks, HMR reloads) classify identically to direct throws.LoginView.handleLoginError's legacy fallback so both entry points classify the same until the webserver surfaces the probetypethroughclient.token_login(tracked byTODO(user-tunable)).Changes
react/src/helper/loginSessionAuth.tsTokenLoginFailedErrorclass and structured throw fromtokenLogin.extraParamstype widened toRecord<string, string | boolean>soforce: truecan pass through alongside string URL params.react/src/components/STokenLoginBoundary.tsxSTokenLoginErrorunion:totp-requiredadded,concurrent-sessionwired up end-to-end.pendingOtpRef(single-use) +forceApprovedRef(sticky) retry state.retryWithOtp/retryWithForcehandlers; existingretry()preserved for non-interactive kinds.DefaultErrorCardgainsonSubmitOtp/onConfirmForceplumbing and kind-branched action area. Cardstatusshifts towarningfor the two interactive kinds.TotpInlineFormsubcomponent (kept co-located in the same file, per scope request).resources/i18n/en.json,resources/i18n/ko.jsonErrorTotpRequiredTitle/ErrorTotpRequiredDescription/ErrorTotpInvalidHint/TotpPlaceholder/SubmitOtp/ForceLogin.ErrorConcurrentSessionDescriptionupdated to match the new force-confirm UX.Test plan
bash scripts/verify.sh→ALL PASSpnpm --dir react jest src/components/STokenLoginBoundary.test.tsx→ 9 passed/?sToken=<TOTP-required>→ card shows OTP input, submit retries withotp=…. Wrong code surfaces invalid hint; right code proceeds.force=true.active-login-session-existsthenrequire-totp-authenticationon the force retry → force flag stays on while OTP is submitted.Follow-ups
typeequality onceclient.token_loginsurfaces the probetypeto the caller.TokenLoginFailedError.failTypeis already reserved.fw:i18nskill.